package com.budwk.app.sys.services.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.budwk.app.common.component.LinuxPackageLayComponent;
import com.budwk.app.common.config.AppException;
import com.budwk.app.sqfw.component.CommonComponent;
import com.budwk.app.sys.models.LinuxPackageLay;
import com.budwk.app.sys.models.LinuxServiceLayConfig;
import com.budwk.app.sys.models.Sys_user;
import com.budwk.app.sys.services.LinuxPackageLayService;
import com.budwk.app.sys.services.LinuxServicelayConfigService;
import com.budwk.app.sys.services.SysUserService;
import com.budwk.app.sys.vo.LinuxPackageLayVo;
import com.budwk.starter.common.page.Pagination;
import com.budwk.starter.common.result.Result;
import com.budwk.starter.database.service.BaseServiceImpl;
import com.budwk.starter.security.utils.SecurityUtil;
import com.jcraft.jsch.Session;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.sql.Criteria;
import org.nutz.dao.util.cri.SqlExpressionGroup;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.upload.TempFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@IocBean(args = {"refer:dao"})
@Slf4j
public class LinuxPackageLayServiceImpl extends BaseServiceImpl<LinuxPackageLay> implements LinuxPackageLayService {
    public LinuxPackageLayServiceImpl(Dao dao) {
        super(dao);
    }

    @Inject
    private LinuxServicelayConfigService linuxServicelayConfigService;
    @Inject
    private SysUserService sysUserService;
    @Inject
    private LinuxPackageLayComponent linuxPackageLayComponent;

    @Override
    public Result<?> uploadFileToLinux(TempFile tempFile, String configId) {
        // 1 参数校验
        linuxPackageLayComponent.checkUploadFileToLinuxParam(tempFile, configId);
        LinuxServiceLayConfig config = linuxServicelayConfigService.fetch(configId);
        // 2 上传数据
        String[] fileNames = tempFile.getSubmittedFileName().split("\\.");
        // 修改文件名 得到新文件名
        String fileNewName = fileNames[0] + "-" + DateUtil.format(new Date(), DatePattern.PURE_DATETIME_PATTERN) + "." + fileNames[1];
        String backFileName = linuxPackageLayComponent.getBackFileName(config, tempFile);
        boolean isUpload = linuxPackageLayComponent.uploadFile(tempFile.getFile(), config, backFileName, fileNewName);
        // 3 记录本地数据
        Sys_user user = sysUserService.fetch(SecurityUtil.getUserId());
        LinuxPackageLay lay = new LinuxPackageLay();
        lay.setCreateId(user.getId())
                .setCreateTime(LocalDateTime.now())
                .setCreateName(user.getUsername())
                .setFileName(fileNewName)
                .setOldFileName(backFileName)
                .setConfigId(config.getId())
                .setPushStatus(isUpload ? 1 : 0)
                .setPath(config.getFileUploadPath());
        this.insert(lay);
        return Result.success("操作成功，请稍后在列表查看上传结果");
    }

    @Override
    public Result<?> listByPage(LinuxPackageLayVo layVo) {
        Criteria cri = Cnd.cri();
        SqlExpressionGroup criSql = cri.where();
        cri.getOrderBy().desc(LinuxPackageLay::getCreateTime);
        Pagination pagination = listPage(layVo.getPageNo(), layVo.getPageSize(), cri);
        // 组装数据
        int totalCount = pagination.getTotalCount();
        if (totalCount == 0) {
            return Result.data(pagination);
        }
        List<LinuxPackageLay> list = pagination.getList(LinuxPackageLay.class);
        // 取出所有的配置id
        List<String> configIds = list.stream().map(LinuxPackageLay::getConfigId).distinct().collect(Collectors.toList());
        // 查询配置表数据
        List<LinuxServiceLayConfig> configs = linuxServicelayConfigService.query(Cnd.where(LinuxServiceLayConfig::getId, "in", configIds));
        List<LinuxPackageLayVo> results = new ArrayList();
        // 组装数据
        for (LinuxPackageLay lay : list) {
            LinuxPackageLayVo linuxPackageLayVo = new LinuxPackageLayVo();
            BeanUtil.copyProperties(lay, linuxPackageLayVo);
            Optional<LinuxServiceLayConfig> configOptional = configs.stream().filter(e -> e.getId().equals(lay.getConfigId())).findFirst();
            if (configOptional.isPresent()) {
                LinuxServiceLayConfig config = configOptional.get();
                String serviceName = String.format("%s%s%s%s", config.getName(), "[", config.getIp(), "]");
                linuxPackageLayVo.setServiceName(serviceName).setType(config.getType());
            }
            results.add(linuxPackageLayVo);
        }
        pagination.setList(results);
        return Result.data(pagination);
    }

    @Override
    public Result<?> pushPackage(String layId, String configId) throws InterruptedException {
        // 1 校验基本参数
        if (StringUtils.isEmpty(layId) || StringUtils.isEmpty(configId)) {
            throw new AppException("请上传参数!!");
        }
        LinuxServiceLayConfig config = linuxServicelayConfigService.fetch(configId);
        if (config == null) {
            throw new AppException("请选择发布的服务器");
        }
        if (StringUtils.isEmpty(config.getShellCommand())) {
            throw new AppException("请先配置发布（启动）脚本");
        }
        // 2 记录发布信息
        addPushResult(layId, 2);
        // 3 发布
        boolean isLocalServer = linuxPackageLayComponent.judgeIsLocalServer(config); // 判断是否为当前服务器
        // 3.1 本地服务器发布
        if (isLocalServer) {
            Boolean isPush = linuxPackageLayComponent.pushToLocalServer(config);
            if (!isPush) {
                addPushResult(layId, 5);
                return Result.error("发布失败，请重新发布！");
            }
            return Result.success();
        }
        // 3.2 外部服务器发布
        Boolean isPush = linuxPackageLayComponent.pushToExternalServer(config);
        if (!isPush) {
            addPushResult(layId, 5);
            return Result.error("发布失败，请重新发布！");
        }
        return Result.success();
    }

    @Override
    public void queryLog(String layId, HttpServletResponse response) throws IOException {
        if (StringUtils.isEmpty(layId)) {
            throw new AppException("请传递参数!");
        }
        LinuxPackageLay packageLay = fetch(layId);
        String configId = packageLay.getConfigId();
        LinuxServiceLayConfig config = linuxServicelayConfigService.fetch(configId);
        String projectPackname = config.getProjectPackname();
        String logName = projectPackname.replace(projectPackname.substring(projectPackname.lastIndexOf(".") + 1), "log");
        String filepath = String.format("%s%s%s", config.getFileUploadPath(), File.separator, logName);
        // 下载文件
        CommonComponent.downFile(response, filepath, logName);
    }

    @Override
    public Result<?> listHistroyLog(String configId, LocalDate queryTime) {
        if (StringUtils.isEmpty(configId)) {
            throw new AppException("请先选择对应的服务器!");
        }
        LinuxServiceLayConfig config = linuxServicelayConfigService.fetch(configId);
        return linuxPackageLayComponent.listHistroyLog(config, queryTime);
    }

    @Override
    public void downloadHistroyLog(String configId, String logName, HttpServletResponse response) {
        if (StringUtils.isEmpty(configId)) {
            throw new AppException("请先选择对应的服务器!");
        }
        LinuxServiceLayConfig config = linuxServicelayConfigService.fetch(configId);
        if (StringUtils.isEmpty(configId)) {
            throw new AppException("请先选择对应的服务器!");
        }
        String filepath = String.format("%s%s%s%s%s", config.getFileUploadPath(), File.separator, "logs", File.separator, logName);
        // 设置输出的格式
        response.addHeader("Content-Disposition","attachment;filename=" + new String(logName.getBytes(), StandardCharsets.ISO_8859_1));
        linuxPackageLayComponent.downloadHistroyLog(response, filepath, config, logName);
    }

    @Override
    public Result<?> afreshUploadFile(String layId) {
        LinuxPackageLay packageLay = this.fetch(layId);
        // 需要重新上传的文件
        File uploadFile = new File(String.format("%s%s%s", packageLay.getPath(), File.separator, packageLay.getFileName()));
        LinuxServiceLayConfig config = linuxServicelayConfigService.fetch(packageLay.getConfigId());
        if (linuxPackageLayComponent.judgeIsLocalServer(config)) {
            // 本地上传
            if (!uploadFile.exists()) {
                throw new AppException("文件已损坏，请删除该条数据后重新上传！");
            }
        }
        boolean isUpload = true; // 是否上传成功
        try {
            // 2 上传到外部服务器
            Session session = linuxServicelayConfigService.getJSchSession(config);
            linuxServicelayConfigService.uploadFile(session, uploadFile, config.getId(), null, config.getProjectPackname());
        } catch (Exception e) {
            log.error("uploadFile error:", e);
            isUpload = false;
        }
        if (isUpload) {
            uploadFile.deleteOnExit();
            addPushResult(packageLay.getId(), 1);
            return Result.success();
        }
        addPushResult(packageLay.getId(), 0);
        return Result.error();
    }

    public void addPushResult(String layId, Integer pushStatus) {
        LinuxPackageLay lay = fetch(layId);
        lay.setPushTime(LocalDateTime.now()).setPusherId(SecurityUtil.getUserId()).setPusherName(SecurityUtil.getUserUsername()).setPushStatus(pushStatus);
        updateIgnoreNull(lay);
    }
}
